1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.base;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  
21  import com.google.common.annotations.Beta;
22  
23  import java.io.PrintWriter;
24  import java.io.StringWriter;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  
29  import javax.annotation.Nullable;
30  
31  /**
32   * Static utility methods pertaining to instances of {@link Throwable}.
33   *
34   * <p>See the Guava User Guide entry on <a href=
35   * "http://code.google.com/p/guava-libraries/wiki/ThrowablesExplained">
36   * Throwables</a>.
37   *
38   * @author Kevin Bourrillion
39   * @author Ben Yu
40   * @since 1.0
41   */
42  public final class Throwables {
43    private Throwables() {}
44  
45    /**
46     * Propagates {@code throwable} exactly as-is, if and only if it is an
47     * instance of {@code declaredType}.  Example usage:
48     * <pre>
49     *   try {
50     *     someMethodThatCouldThrowAnything();
51     *   } catch (IKnowWhatToDoWithThisException e) {
52     *     handle(e);
53     *   } catch (Throwable t) {
54     *     Throwables.propagateIfInstanceOf(t, IOException.class);
55     *     Throwables.propagateIfInstanceOf(t, SQLException.class);
56     *     throw Throwables.propagate(t);
57     *   }
58     * </pre>
59     */
60    public static <X extends Throwable> void propagateIfInstanceOf(
61        @Nullable Throwable throwable, Class<X> declaredType) throws X {
62      // Check for null is needed to avoid frequent JNI calls to isInstance().
63      if (throwable != null && declaredType.isInstance(throwable)) {
64        throw declaredType.cast(throwable);
65      }
66    }
67  
68    /**
69     * Propagates {@code throwable} exactly as-is, if and only if it is an
70     * instance of {@link RuntimeException} or {@link Error}.  Example usage:
71     * <pre>
72     *   try {
73     *     someMethodThatCouldThrowAnything();
74     *   } catch (IKnowWhatToDoWithThisException e) {
75     *     handle(e);
76     *   } catch (Throwable t) {
77     *     Throwables.propagateIfPossible(t);
78     *     throw new RuntimeException("unexpected", t);
79     *   }
80     * </pre>
81     */
82    public static void propagateIfPossible(@Nullable Throwable throwable) {
83      propagateIfInstanceOf(throwable, Error.class);
84      propagateIfInstanceOf(throwable, RuntimeException.class);
85    }
86  
87    /**
88     * Propagates {@code throwable} exactly as-is, if and only if it is an
89     * instance of {@link RuntimeException}, {@link Error}, or
90     * {@code declaredType}. Example usage:
91     * <pre>
92     *   try {
93     *     someMethodThatCouldThrowAnything();
94     *   } catch (IKnowWhatToDoWithThisException e) {
95     *     handle(e);
96     *   } catch (Throwable t) {
97     *     Throwables.propagateIfPossible(t, OtherException.class);
98     *     throw new RuntimeException("unexpected", t);
99     *   }
100    * </pre>
101    *
102    * @param throwable the Throwable to possibly propagate
103    * @param declaredType the single checked exception type declared by the
104    *     calling method
105    */
106   public static <X extends Throwable> void propagateIfPossible(
107       @Nullable Throwable throwable, Class<X> declaredType) throws X {
108     propagateIfInstanceOf(throwable, declaredType);
109     propagateIfPossible(throwable);
110   }
111 
112   /**
113    * Propagates {@code throwable} exactly as-is, if and only if it is an
114    * instance of {@link RuntimeException}, {@link Error}, {@code declaredType1},
115    * or {@code declaredType2}.  In the unlikely case that you have three or more
116    * declared checked exception types, you can handle them all by invoking these
117    * methods repeatedly. See usage example in {@link
118    * #propagateIfPossible(Throwable, Class)}.
119    *
120    * @param throwable the Throwable to possibly propagate
121    * @param declaredType1 any checked exception type declared by the calling
122    *     method
123    * @param declaredType2 any other checked exception type declared by the
124    *     calling method
125    */
126   public static <X1 extends Throwable, X2 extends Throwable>
127       void propagateIfPossible(@Nullable Throwable throwable,
128           Class<X1> declaredType1, Class<X2> declaredType2) throws X1, X2 {
129     checkNotNull(declaredType2);
130     propagateIfInstanceOf(throwable, declaredType1);
131     propagateIfPossible(throwable, declaredType2);
132   }
133 
134   /**
135    * Propagates {@code throwable} as-is if it is an instance of
136    * {@link RuntimeException} or {@link Error}, or else as a last resort, wraps
137    * it in a {@code RuntimeException} then propagates.
138    * <p>
139    * This method always throws an exception. The {@code RuntimeException} return
140    * type is only for client code to make Java type system happy in case a
141    * return value is required by the enclosing method. Example usage:
142    * <pre>
143    *   T doSomething() {
144    *     try {
145    *       return someMethodThatCouldThrowAnything();
146    *     } catch (IKnowWhatToDoWithThisException e) {
147    *       return handle(e);
148    *     } catch (Throwable t) {
149    *       throw Throwables.propagate(t);
150    *     }
151    *   }
152    * </pre>
153    *
154    * @param throwable the Throwable to propagate
155    * @return nothing will ever be returned; this return type is only for your
156    *     convenience, as illustrated in the example above
157    */
158   public static RuntimeException propagate(Throwable throwable) {
159     propagateIfPossible(checkNotNull(throwable));
160     throw new RuntimeException(throwable);
161   }
162 
163   /**
164    * Returns the innermost cause of {@code throwable}. The first throwable in a
165    * chain provides context from when the error or exception was initially
166    * detected. Example usage:
167    * <pre>
168    *   assertEquals("Unable to assign a customer id",
169    *       Throwables.getRootCause(e).getMessage());
170    * </pre>
171    */
172   public static Throwable getRootCause(Throwable throwable) {
173     Throwable cause;
174     while ((cause = throwable.getCause()) != null) {
175       throwable = cause;
176     }
177     return throwable;
178   }
179 
180   /**
181    * Gets a {@code Throwable} cause chain as a list.  The first entry in the
182    * list will be {@code throwable} followed by its cause hierarchy.  Note
183    * that this is a snapshot of the cause chain and will not reflect
184    * any subsequent changes to the cause chain.
185    *
186    * <p>Here's an example of how it can be used to find specific types
187    * of exceptions in the cause chain:
188    *
189    * <pre>
190    * Iterables.filter(Throwables.getCausalChain(e), IOException.class));
191    * </pre>
192    *
193    * @param throwable the non-null {@code Throwable} to extract causes from
194    * @return an unmodifiable list containing the cause chain starting with
195    *     {@code throwable}
196    */
197   @Beta // TODO(kevinb): decide best return type
198   public static List<Throwable> getCausalChain(Throwable throwable) {
199     checkNotNull(throwable);
200     List<Throwable> causes = new ArrayList<Throwable>(4);
201     while (throwable != null) {
202       causes.add(throwable);
203       throwable = throwable.getCause();
204     }
205     return Collections.unmodifiableList(causes);
206   }
207 
208   /**
209    * Returns a string containing the result of
210    * {@link Throwable#toString() toString()}, followed by the full, recursive
211    * stack trace of {@code throwable}. Note that you probably should not be
212    * parsing the resulting string; if you need programmatic access to the stack
213    * frames, you can call {@link Throwable#getStackTrace()}.
214    */
215   public static String getStackTraceAsString(Throwable throwable) {
216     StringWriter stringWriter = new StringWriter();
217     throwable.printStackTrace(new PrintWriter(stringWriter));
218     return stringWriter.toString();
219   }
220 }